/*
 *  Copyright 2012 yura.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */

package org.opu.odg.dg.incomingdata;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.opu.odg.dg.impl.DocumentGenerationContext;
import org.opu.odg.dg.runtimedata.VariableResolver;
import org.opu.odg.dg.runtimedata.VariableHolder;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;

/**
 *
 * @author yura
 */
@Root(name="database")
public class DatabaseContentDescription extends ContentDescription {

    @Element(required=false)
    private DatabaseSettings databaseSettings;
    @Element(data=true)
    private String query;
    private Statement statement;

    private DatabaseContentDescription() {
        // for simple framework
    }

    public DatabaseContentDescription(DatabaseSettings databaseSettings, String query) {
        this.databaseSettings = databaseSettings != null
                && databaseSettings.isEmpty() ? null : databaseSettings;
        this.query = query;
    }

    @Override
    public Iterable<ContextIteration> asIterable(DocumentGenerationContext context,
            VariableHolder variableHolder) {
        Connection connection = context.getConnectionHolder().getConnection(databaseSettings);
        final ResultSet rs;
        try {
            statement = connection.createStatement();
            query = VariableResolver.assignVarValue(query, variableHolder);
            rs = statement.executeQuery(query);
        } catch (SQLException ex) {
            throw new IllegalArgumentException("Can't execute query: " + query + ": " + ex);
        }

        return new Iterable<ContextIteration>() {

            @Override
            public Iterator<ContextIteration> iterator() {
                return new Iterator<ContextIteration>() {

                    private int iteration = 0;

                    @Override
                    public boolean hasNext() {
                        try {
                            return rs.next();
                        } catch (SQLException ex) {
                            throw new IllegalArgumentException(ex);
                        }
                    }

                    @Override
                    public ContextIteration next() {
                        iteration ++;
                        return new ContextIteration() {

                            @Override
                            public String get(String name) {
                                if (ContextIteration.ITERATION.equals(name)) {
                                    return String.valueOf(iteration());
                                }

                                try {
                                    return rs.getString(name);
                                } catch (SQLException ex) {
                                    return null;
                                }
                            }

                            @Override
                            public String get(int index) {
                                try {
                                    return rs.getString(index);
                                } catch (SQLException ex) {
                                    return null;
                                }
                            }

                            @Override
                            public Collection<String> getAll() {
                                try {
                                    ResultSetMetaData metaData = rs.getMetaData();
                                    int cc = metaData.getColumnCount();
                                    List<String> list = new ArrayList<String>(cc);
                                    for(int i = 1; i <= cc; i++) {
                                        list.add(rs.getString(i));
                                    }
                                    return list;
                                } catch (SQLException ex) {
                                    return Collections.emptyList();
                                }
                            }

                            @Override
                            public int iteration() {
                                return iteration;
                            }
                        };
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException("Not supported.");
                    }
                };
            }
        };
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final DatabaseContentDescription other = (DatabaseContentDescription) obj;
        if (this.databaseSettings != other.databaseSettings && (this.databaseSettings == null || !this.databaseSettings.equals(other.databaseSettings))) {
            return false;
        }
        if ((this.query == null) ? (other.query != null) : !this.query.equals(other.query)) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 17 * hash + (this.databaseSettings != null ? this.databaseSettings.hashCode() : 0);
        hash = 17 * hash + (this.query != null ? this.query.hashCode() : 0);
        return hash;
    }

    @Override
    public void close() throws IOException {
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException ex) {
            }
        }
    }

    @Override
    public ContentDescription clone() {
        return new DatabaseContentDescription(databaseSettings != null 
                ? databaseSettings.clone() : null, query);
    }

    public DatabaseSettings getDatabaseSettings() {
        return databaseSettings;
    }

    public String getQuery() {
        return query;
    }
}
